import { Op } from 'sequelize';
import Comment from '../models/Comment';
import User from '../models/User';
import Article from '../models/Article';
import { redisClient } from '../database';
import DOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';

// 创建DOMPurify实例（用于XSS过滤）
const window = new JSDOM('').window;
const purify = DOMPurify(window);

// 评论查询参数接口
interface CommentQueryParams {
  article_id?: string;
  user_id?: string;
  status?: 'pending' | 'approved' | 'rejected';
  parent_id?: string | null;
  keyword?: string;
  sort_by?: string;
  sort_order?: 'ASC' | 'DESC';
  page?: number;
  limit?: number;
  author_ip?: string;
  author_email?: string;
}

// 评论创建接口
interface CommentCreateParams {
  content: string;
  article_id: string;
  user_id?: string;
  parent_id?: string;
  author_name?: string;
  author_email?: string;
  author_ip?: string;
  author_user_agent?: string;
  status?: 'pending' | 'approved' | 'rejected';
}

// 评论更新接口
interface CommentUpdateParams {
  content?: string;
  status?: 'pending' | 'approved' | 'rejected';
  reject_reason?: string;
  is_pinned?: boolean;
  is_hidden?: boolean;
}

// 敏感词过滤接口
interface SensitiveWordResult {
  hasSensitiveWords: boolean;
  filteredContent: string;
  matchedWords: string[];
}

class CommentService {
  // 敏感词列表（实际应用中可以从数据库或配置文件加载）
  private sensitiveWords = [
    '赌博', '博彩', '色情', '暴力', '政治', '违禁药品',
    'casino', 'porn', 'sex', 'violent', 'drug'
  ];
  
  /**
   * 获取评论列表
   * @param params 查询参数
   * @returns 评论列表和总数
   */
  async getComments(params: CommentQueryParams) {
    const {
      article_id,
      user_id,
      status,
      parent_id,
      keyword,
      sort_by = 'created_at',
      sort_order = 'DESC',
      page = 1,
      limit = 20,
      author_ip,
      author_email,
    } = params;
    
    const offset = (page - 1) * limit;
    
    // 构建查询条件
    const where: any = {};
    
    if (article_id) {
      where.article_id = article_id;
    }
    
    if (user_id) {
      where.user_id = user_id;
    }
    
    if (status) {
      where.status = status;
    }
    
    if (parent_id !== undefined) {
      where.parent_id = parent_id;
    }
    
    if (keyword) {
      where[Op.or] = [
        { content: { [Op.like]: `%${keyword}%` } },
        { author_name: { [Op.like]: `%${keyword}%` } },
        { author_email: { [Op.like]: `%${keyword}%` } },
      ];
    }
    
    if (author_ip) {
      where.author_ip = author_ip;
    }
    
    if (author_email) {
      where.author_email = author_email;
    }
    
    // 执行查询
    const { rows, count } = await Comment.findAndCountAll({
      where,
      include: [
        {
          model: User,
          as: 'user',
          attributes: ['id', 'username', 'nickname', 'avatar'],
        },
        {
          model: Article,
          as: 'article',
          attributes: ['id', 'title', 'slug'],
        },
        {
          model: User,
          as: 'reviewer',
          attributes: ['id', 'username', 'nickname'],
        },
        {
          model: Comment,
          as: 'parent',
          attributes: ['id', 'content', 'author_name'],
          include: [
            {
              model: User,
              as: 'user',
              attributes: ['id', 'username', 'nickname', 'avatar'],
            },
          ],
        },
      ],
      order: [[sort_by, sort_order]],
      limit,
      offset,
    });
    
    return {
      comments: rows,
      total: count,
      page,
      limit,
      total_pages: Math.ceil(count / limit),
    };
  }
  
  /**
   * 获取文章评论树
   * @param articleId 文章ID
   * @param onlyApproved 是否只返回已通过评论
   * @returns 树形结构的评论列表
   */
  async getArticleCommentTree(articleId: string, onlyApproved = true) {
    // 先尝试从缓存获取
    const cacheKey = `article:${articleId}:comments${onlyApproved ? ':approved' : ''}`;
    const cachedComments = await redisClient.get(cacheKey);
    
    if (cachedComments) {
      return JSON.parse(cachedComments);
    }
    
    // 构建查询条件
    const where: any = {
      article_id: articleId,
    };
    
    if (onlyApproved) {
      where.status = 'approved';
    }
    
    // 获取所有评论
    const comments = await Comment.findAll({
      where,
      include: [
        {
          model: User,
          as: 'user',
          attributes: ['id', 'username', 'nickname', 'avatar'],
        },
      ],
      order: [
        ['is_pinned', 'DESC'],
        ['created_at', 'ASC'],
      ],
    });
    
    // 转换为树形结构
    const commentTree = this.buildCommentTree(comments);
    
    // 缓存评论树
    await redisClient.set(cacheKey, JSON.stringify(commentTree), {
      EX: 3600, // 缓存1小时
    });
    
    return commentTree;
  }
  
  /**
   * 构建评论树形结构
   * @param comments 评论列表
   * @param parentId 父评论ID
   * @returns 树形结构的评论列表
   */
  private buildCommentTree(comments: any[], parentId: string | null = null) {
    const result: any[] = [];
    
    comments.forEach(comment => {
      if (comment.parent_id === parentId) {
        // 隐藏评论内容（如果设置了隐藏）
        if (comment.is_hidden) {
          comment.content = '该评论已被隐藏';
        }
        
        // 递归获取子评论
        const children = this.buildCommentTree(comments, comment.id);
        if (children.length > 0) {
          comment.setDataValue('children', children);
          comment.setDataValue('reply_count', children.length);
        } else {
          comment.setDataValue('children', []);
          comment.setDataValue('reply_count', 0);
        }
        
        result.push(comment);
      }
    });
    
    return result;
  }
  
  /**
   * 根据ID获取评论详情
   * @param id 评论ID
   * @returns 评论详情
   */
  async getCommentById(id: string) {
    const comment = await Comment.findByPk(id, {
      include: [
        {
          model: User,
          as: 'user',
          attributes: ['id', 'username', 'nickname', 'avatar'],
        },
        {
          model: Article,
          as: 'article',
          attributes: ['id', 'title', 'slug'],
        },
        {
          model: User,
          as: 'reviewer',
          attributes: ['id', 'username', 'nickname'],
        },
        {
          model: Comment,
          as: 'parent',
          attributes: ['id', 'content', 'author_name'],
          include: [
            {
              model: User,
              as: 'user',
              attributes: ['id', 'username', 'nickname', 'avatar'],
            },
          ],
        },
      ],
    });
    
    if (!comment) {
      throw new Error('评论不存在');
    }
    
    return comment;
  }
  
  /**
   * 创建评论
   * @param params 评论参数
   * @returns 创建的评论
   */
  async createComment(params: CommentCreateParams) {
    const {
      content,
      article_id,
      user_id,
      parent_id,
      author_name,
      author_email,
      author_ip,
      author_user_agent,
      status = 'pending', // 默认为待审核
    } = params;
    
    // 检查文章是否存在
    const article = await Article.findByPk(article_id);
    if (!article) {
      throw new Error('文章不存在');
    }
    
    // 检查父评论是否存在
    if (parent_id) {
      const parentComment = await Comment.findByPk(parent_id);
      if (!parentComment) {
        throw new Error('父评论不存在');
      }
      
      // 如果父评论已经是回复，不允许再次回复（最多2层）
      if (parentComment.parent_id) {
        throw new Error('不允许对回复再次回复');
      }
    }
    
    // 检查用户是否存在
    if (user_id) {
      const user = await User.findByPk(user_id);
      if (!user) {
        throw new Error('用户不存在');
      }
    }
    
    // 内容过滤和敏感词检查
    const sanitizedContent = purify.sanitize(content);
    const { hasSensitiveWords, filteredContent, matchedWords } = 
      this.filterSensitiveWords(sanitizedContent);
    
    // 创建评论
    const comment = await Comment.create({
      content: filteredContent,
      article_id,
      user_id,
      parent_id,
      author_name: user_id ? undefined : author_name,
      author_email: user_id ? undefined : author_email,
      author_ip,
      author_user_agent,
      status: hasSensitiveWords ? 'pending' : status, // 包含敏感词时强制设为待审核
      has_sensitive_words: hasSensitiveWords,
      matched_sensitive_words: hasSensitiveWords ? matchedWords.join(',') : null,
    });
    
    // 更新文章评论数
    await this.updateArticleCommentCount(article_id);
    
    // 如果是回复，更新父评论的回复数
    if (parent_id) {
      await this.updateCommentReplyCount(parent_id);
    }
    
    // 清除缓存
    await redisClient.del(`article:${article_id}:comments:approved`);
    await redisClient.del(`article:${article_id}:comments`);
    
    // 加载关联数据
    const createdComment = await this.getCommentById(comment.id);
    
    return {
      comment: createdComment,
      has_sensitive_words: hasSensitiveWords,
      matched_words: matchedWords,
    };
  }
  
  /**
   * 更新评论
   * @param id 评论ID
   * @param params 更新参数
   * @param reviewerId 审核人ID
   * @returns 更新后的评论
   */
  async updateComment(id: string, params: CommentUpdateParams, reviewerId?: string) {
    const comment = await Comment.findByPk(id);
    
    if (!comment) {
      throw new Error('评论不存在');
    }
    
    const {
      content,
      status,
      reject_reason,
      is_pinned,
      is_hidden,
    } = params;
    
    // 准备更新数据
    const updateData: any = {};
    
    // 如果更新内容，需要过滤和检查敏感词
    if (content !== undefined) {
      const sanitizedContent = purify.sanitize(content);
      const { filteredContent, hasSensitiveWords, matchedWords } = 
        this.filterSensitiveWords(sanitizedContent);
      
      updateData.content = filteredContent;
      updateData.has_sensitive_words = hasSensitiveWords;
      updateData.matched_sensitive_words = hasSensitiveWords ? matchedWords.join(',') : null;
    }
    
    // 如果更新状态，记录审核信息
    if (status !== undefined && status !== comment.status) {
      updateData.status = status;
      
      if (reviewerId) {
        updateData.reviewed_by = reviewerId;
        updateData.reviewed_at = new Date();
      }
      
      if (status === 'rejected' && reject_reason) {
        updateData.reject_reason = reject_reason;
      }
    }
    
    if (is_pinned !== undefined) updateData.is_pinned = is_pinned;
    if (is_hidden !== undefined) updateData.is_hidden = is_hidden;
    
    // 更新评论
    await comment.update(updateData);
    
    // 清除缓存
    await redisClient.del(`article:${comment.article_id}:comments:approved`);
    await redisClient.del(`article:${comment.article_id}:comments`);
    
    // 如果状态变更，可能需要更新文章评论数
    if (status !== undefined && status !== comment.status) {
      await this.updateArticleCommentCount(comment.article_id);
    }
    
    // 重新加载评论
    const updatedComment = await this.getCommentById(id);
    
    return updatedComment;
  }
  
  /**
   * 批量更新评论状态
   * @param ids 评论ID数组
   * @param status 新状态
   * @param reviewerId 审核人ID
   * @param rejectReason 拒绝原因
   * @returns 更新结果
   */
  async updateCommentsStatus(
    ids: string[],
    status: 'pending' | 'approved' | 'rejected',
    reviewerId?: string,
    rejectReason?: string
  ) {
    if (!ids.length) {
      throw new Error('请选择要更新的评论');
    }
    
    // 准备更新数据
    const updateData: any = {
      status,
    };
    
    if (reviewerId) {
      updateData.reviewed_by = reviewerId;
      updateData.reviewed_at = new Date();
    }
    
    if (status === 'rejected' && rejectReason) {
      updateData.reject_reason = rejectReason;
    }
    
    // 获取评论的文章ID，用于后续清除缓存
    const comments = await Comment.findAll({
      where: { id: { [Op.in]: ids } },
      attributes: ['id', 'article_id'],
    });
    
    const articleIds = [...new Set(comments.map(comment => comment.article_id))];
    
    // 批量更新评论
    await Comment.update(updateData, {
      where: { id: { [Op.in]: ids } },
    });
    
    // 清除缓存
    for (const articleId of articleIds) {
      await redisClient.del(`article:${articleId}:comments:approved`);
      await redisClient.del(`article:${articleId}:comments`);
      await this.updateArticleCommentCount(articleId);
    }
    
    return {
      success: true,
      message: `成功更新${ids.length}条评论状态为${status}`,
    };
  }
  
  /**
   * 删除评论
   * @param id 评论ID
   * @returns 删除结果
   */
  async deleteComment(id: string) {
    const comment = await Comment.findByPk(id, {
      include: [
        {
          model: Comment,
          as: 'replies',
          attributes: ['id'],
        },
      ],
    });
    
    if (!comment) {
      throw new Error('评论不存在');
    }
    
    const articleId = comment.article_id;
    const parentId = comment.parent_id;
    
    // 如果有回复，则需要将其设置为软删除
    if (comment.replies && comment.replies.length > 0) {
      await comment.update({
        content: '该评论已被删除',
        is_hidden: true,
        deleted_at: new Date(),
      });
    } else {
      // 没有回复，可以直接删除
      await comment.destroy();
    }
    
    // 清除缓存
    await redisClient.del(`article:${articleId}:comments:approved`);
    await redisClient.del(`article:${articleId}:comments`);
    
    // 更新文章评论数
    await this.updateArticleCommentCount(articleId);
    
    // 如果是回复，更新父评论的回复数
    if (parentId) {
      await this.updateCommentReplyCount(parentId);
    }
    
    return { success: true, message: '删除评论成功' };
  }
  
  /**
   * 批量删除评论
   * @param ids 评论ID数组
   * @returns 删除结果
   */
  async deleteCommentsBatch(ids: string[]) {
    if (!ids.length) {
      throw new Error('请选择要删除的评论');
    }
    
    // 获取评论详情
    const comments = await Comment.findAll({
      where: { id: { [Op.in]: ids } },
      include: [
        {
          model: Comment,
          as: 'replies',
          attributes: ['id'],
        },
      ],
    });
    
    // 收集文章ID和父评论ID，用于后续更新
    const articleIds = new Set<string>();
    const parentIds = new Set<string>();
    
    // 分为两类：有回复的评论和无回复的评论
    const commentsWithReplies: string[] = [];
    const commentsWithoutReplies: string[] = [];
    
    comments.forEach(comment => {
      articleIds.add(comment.article_id);
      if (comment.parent_id) parentIds.add(comment.parent_id);
      
      if (comment.replies && comment.replies.length > 0) {
        commentsWithReplies.push(comment.id);
      } else {
        commentsWithoutReplies.push(comment.id);
      }
    });
    
    const transaction = await Comment.sequelize!.transaction();
    
    try {
      // 对有回复的评论进行软删除
      if (commentsWithReplies.length > 0) {
        await Comment.update(
          {
            content: '该评论已被删除',
            is_hidden: true,
            deleted_at: new Date(),
          },
          {
            where: { id: { [Op.in]: commentsWithReplies } },
            transaction,
          }
        );
      }
      
      // 对无回复的评论进行物理删除
      if (commentsWithoutReplies.length > 0) {
        await Comment.destroy({
          where: { id: { [Op.in]: commentsWithoutReplies } },
          transaction,
        });
      }
      
      await transaction.commit();
      
      // 清除缓存并更新计数
      for (const articleId of articleIds) {
        await redisClient.del(`article:${articleId}:comments:approved`);
        await redisClient.del(`article:${articleId}:comments`);
        await this.updateArticleCommentCount(articleId);
      }
      
      for (const parentId of parentIds) {
        await this.updateCommentReplyCount(parentId);
      }
      
      return {
        success: true,
        message: `成功删除${ids.length}条评论`,
        soft_deleted: commentsWithReplies.length,
        hard_deleted: commentsWithoutReplies.length,
      };
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }
  
  /**
   * 更新文章评论数
   * @param articleId 文章ID
   */
  private async updateArticleCommentCount(articleId: string) {
    const approvedCount = await Comment.count({
      where: {
        article_id: articleId,
        status: 'approved',
      },
    });
    
    await Article.update(
      { comment_count: approvedCount },
      { where: { id: articleId } }
    );
  }
  
  /**
   * 更新评论回复数
   * @param commentId 评论ID
   */
  private async updateCommentReplyCount(commentId: string) {
    const replyCount = await Comment.count({
      where: {
        parent_id: commentId,
        status: 'approved',
      },
    });
    
    await Comment.update(
      { reply_count: replyCount },
      { where: { id: commentId } }
    );
  }
  
  /**
   * 过滤敏感词
   * @param content 内容
   * @returns 过滤结果
   */
  private filterSensitiveWords(content: string): SensitiveWordResult {
    let filteredContent = content;
    const matchedWords: string[] = [];
    
    // 简单的敏感词过滤
    this.sensitiveWords.forEach(word => {
      if (content.toLowerCase().includes(word.toLowerCase())) {
        matchedWords.push(word);
        // 用星号替换敏感词
        const regex = new RegExp(word, 'gi');
        filteredContent = filteredContent.replace(regex, '*'.repeat(word.length));
      }
    });
    
    return {
      hasSensitiveWords: matchedWords.length > 0,
      filteredContent,
      matchedWords,
    };
  }
  
  /**
   * 获取IP黑名单
   * @returns IP黑名单列表
   */
  async getIPBlacklist() {
    // 查询被拒绝次数较多的IP
    const result = await Comment.sequelize!.query(`
      SELECT author_ip, COUNT(*) as reject_count
      FROM comments
      WHERE status = 'rejected' AND author_ip IS NOT NULL
      GROUP BY author_ip
      HAVING COUNT(*) >= 3
      ORDER BY reject_count DESC
    `);
    
    return result[0];
  }
  
  /**
   * 添加IP到黑名单
   * @param ip IP地址
   * @returns 添加结果
   */
  async addIPToBlacklist(ip: string) {
    // 在实际应用中，这里应该将IP添加到黑名单表中
    // 简化起见，我们直接设置该IP发布的所有评论为拒绝状态
    await Comment.update(
      { status: 'rejected', reject_reason: 'IP已被加入黑名单' },
      { where: { author_ip: ip, status: 'pending' } }
    );
    
    // 清除相关缓存
    const comments = await Comment.findAll({
      where: { author_ip: ip },
      attributes: ['article_id'],
    });
    
    const articleIds = [...new Set(comments.map(comment => comment.article_id))];
    
    for (const articleId of articleIds) {
      await redisClient.del(`article:${articleId}:comments:approved`);
      await redisClient.del(`article:${articleId}:comments`);
      await this.updateArticleCommentCount(articleId);
    }
    
    return { success: true, message: `IP ${ip} 已添加到黑名单` };
  }
}

export default new CommentService(); 